#include "Parser.h"

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

#include <algorithm>

#include <boost/assign/list_of.hpp>


#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/trim.hpp>

#include <boost/lambda/lambda.hpp>

#include <boost/foreach.hpp>

#include "lib.h"

#include "ATL.h"

#include "CodesErreurs.h"

using namespace std;
using namespace boost::assign;
using namespace boost::lambda;
using namespace boost::algorithm;

bool Parser::niveauxLus( false );

const vector< string > Parser::NomsNiveaux =
  list_of( "Parser::elementaire" )( "Parser::fonction" );

DebugLevelType Parser::elementaire( BLANK ),
  Parser::fonction( BLANK );

/**
 * Constructeur par défaut, construit le Parser sans initialiser le
 * nom du fichier à lire.
 */
Parser::Parser()
  : ADDObjetBase( "Parser" ), nomFichier(""), donnees(""), taille(0),
    numeroElement( 0 ), donneesLues( false ), index( 0 )
{
  Parents.push ( 0 );

  if ( ! niveauxLus )
    {
      lireNiveaux( this, NomsNiveaux );
      niveauxLus = true;
    }
}

/**
 * Constructeur permettant d'initialiser directement le nom du
 * fichier à lire.
 *
 * @param[in] nom_fichier nom du fichier à lire
 */
Parser::Parser(const string& nom_fichier)
  : ADDObjetBase( "Parser" ), nomFichier( nom_fichier ), donnees(""),
    taille(0), numeroElement( 0 ), donneesLues( false ), index( 0 )
{
  Parents.push ( 0 );

  if ( ! niveauxLus )
    {
      lireNiveaux( this, NomsNiveaux );
      niveauxLus = true;
    }
}

/**
 * Destructeur, peut afficher la structure de l'arbre si la valeur
 * de Parser::elementaire est fixée à \e INFO dans le fichier
 * CONFIG.
 */
Parser::~Parser()
{
  /*Debug::*/empileNiveau( &elementaire );

  afficherArbre( info() );
  
  for (unsigned i (0); i < numeroElement; i++)
    delete Arbre[i];

  /*Debug::*/depileNiveau();
}

/**
 * Permet de déterminer le nom du fichier d'entrée, si le
 * constructeur par défaut a été utilisé pour la création de
 * l'instance.
 */
void Parser::setNomFichier(const string& nom_fichier)
{
  nomFichier = nom_fichier;
}

/**
 * Charge le contenu du fichier dans la structure de donnée interne
 * (le fichier est mis dans une string, sans retours à la ligne).
 *
 * @return StatusCode \e true si tout s'est bien déroulé, valeur()
 * donne le nombre de lignes. Dans le cas où un problème apparaît,
 * message() est aussi rempli.
 */
StatusCode Parser::chargerFichier()
{
  ifstream fichier;

  fichier.open( nomFichier.c_str() );

  if ( fichier.fail() )
    {
      error() << "Impossible de lire le fichier " << nomFichier << endmsg;
      return StatusCode( false, 0, CodesErreurs::ErreurLectureFichier,
			 "Lecture des données impossible" );
    }

  string ligne;
  unsigned compteur(0);

  while ( !fichier.eof() )
    {
      getline( fichier, ligne );
      trim( ligne );
      donnees += ligne + " ";
      compteur += 1;
    }

  taille = donnees.size();

  donneesLues = true;

  return StatusCode( true, compteur );
}

/**
 * Interface permettant de lire le document XML. La lecture se fait de
 * manière récursive, ce qui rend difficile la gestion de la pile de
 * niveaux de sortie. On appelle donc ici la méthode lireDocRecursif,
 * qui fait tout le travail.
 *
 * @return StatusCode, \e true si tout s'est bien déroulé, \e false
 * sinon.
 */
StatusCode Parser::lireDocument()
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "lireDocument -> début" << endmsg;
  
  StatusCode sc( lireDocRecursif() );

  info() << sc.message() << endmsg;

  func()  << "lireDocument -> fin" << endmsg;
  /*Debug::*/depileNiveau();

  return sc;
}

/**
 * Permet de fixer les deux niveaux de sorties (elementaire et
 * fonction).
 *
 * @param[in] lequel 0 pour Parser::elementaire, 1 pour Parser::fonction
 * @param[in] lvl niveau d'affichage
 */
void Parser::setLevel(const unsigned& lvl, const unsigned& lequel)
{
  switch( lequel )
    {
    case 1:
      elementaire = static_cast< DebugLevelType >( lvl );
      break;
    case 2:
      fonction = static_cast< DebugLevelType >( lvl );
      break;
    default:
      FlotMessage warn( WARN, "Parser" );
      warn << "setLevel Tentative de régler un niveau de sortie inexistant "
		<< lequel << endmsg;
    }
}

/**
 * Permet de récupérer les informations lues, sous forme de
 * map<string,\ string>.
 *
 * @param[out] infosLues map<string,\ string>, qui est remplie par la
 * méthode.
 *
 * @return StatusCode, false si le conteneur est vide.
 */
StatusCode Parser::getInformations(map< string, string >& infosLues) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "getInformations -> début" << endmsg;

  map< string, ElementDOM >::const_iterator It, Begin( Informations.begin() ),
    End( Informations.end() );

  for (It = Begin; It != End; It++)
    {
      if ( It -> second . aDonnees() )
	infosLues[ It -> first ] = It -> second . donnees();
    }

  func() << "getInformations -> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return StatusCode( !(Begin == End) );
}

/**
 * Permet de récupérer l'ordre des balise, afin de recomposer la
 * structure du fichier.
 *
 * @param[out] structLue conteneur où est récupéré l'ordre.
 *
 * @return StatusCode, false si le conteneur est vide.
 */
StatusCode Parser::getStructureFichier(vector< const ElementDOM* >& structLue)
  const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "getStructureFichier -> début" << endmsg;

  structLue.assign( Arbre.begin(), Arbre.end() );
  
  func() << "getStructureFichier -> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return StatusCode( !(Arbre.empty()) );
}

/**
 * Permet de récupérer le premier enfant du document.
 *
 * @return un pointeur sur le premier ElementDOM du document.
 */
const ElementDOM* Parser::premierEnfant(const ElementDOM* elDom) const
{
  if ( elDom == 0 )
    return Arbre.front();
  else
    {
      const vector< const ElementDOM* >* enfants = elDom -> enfants();
      if ( enfants -> empty() )
	return 0;
      else
	return enfants -> front();
    }
}

/**
 * Permet de récupérer le "frère" suivant d'un ElementDOM donné.
 *
 * @param[in] elDom pointeur sur l'élément dont on veut le frère.
 *
 * @return pointeur sur le frère.
 */
const ElementDOM* Parser::prochainFrere(const ElementDOM*& elDom) const
{
  const ElementDOM* parent = elDom -> parent();

  vector< ElementDOM* >::const_iterator it, End( Arbre.end() );

  it = find( Arbre.begin(), End, elDom );
  if ( it == End )
    {
      error() << "L'élément DOM n'existe pas" << endmsg;
      return 0;
    }

  it++;
  while ( it != End )
    {
      if ( (*it) -> parent()  == parent )
	break;
      it++;
    }
  if ( it == End )
    {
      return 0;
    }
  else
    return *it;
}

FlotMessage& Parser::afficherArbre( FlotMessage& flot )
{
  string indentation;

  if ( donneesLues )
    {
      flot << "Affichage : " << Arbre.size() << endmsg;

      for (unsigned i (0), j; i < numeroElement; i++)
	{
	  indentation = "";
	  for (j = 0; j < Arbre[i] -> niveau(); j++)
	    indentation += "  ";
	  flot << indentation << Arbre[i] -> balise() << " - \'"
		 << Arbre[i] -> donnees() << "\'";
	  if ( Arbre[i] -> aAttributs() )
	    {
	      flot << " (";
	      Arbre[i] -> afficheAttributs( flot );
	      flot << ')';
	    }
	  if ( Arbre[i] -> parent() != 0 )
	    flot << " - parent "
		   << Arbre[i] -> parent() -> balise() << endmsg;
	  else
	    flot << " - racine" << endmsg;
	}
    }

  return flot;
}

/**
 * Méthode qui interprète le XML. On procède de manière récursive : si
 * la balise s'ouvre puis se ferme, on va écrire le champ de donnée,
 * ainsi que le "numéro" du champ, qui permet ensuite de reproduire la
 * structure du fichier. Si la balise ne contient pas de données mais
 * d'autres balises, elle est inscrite dans la structure, mais aucune
 * donnée ne lui est associée.\n
 *
 * Les balises sont stockées dans un stack, afin de s'assurer que
 * chaque tag ouvert est bien refermé en respectant la syntaxe XML\n
 *
 * La lecture s'arrête lorsque le stack est vide : c'est à dire
 * lorsque toutes les balises ouvrantes ont été fermées.
 *
 * @return StatusCode \e true si la lecture de l'ElementDOM s'est
 * effectuée sans problème, \e false dans le cas contraire. Un message
 * peut être associé.
 */
StatusCode Parser::lireDocRecursif()
{
  StatusCode sc;

  if ( ! donneesLues )
    sc = chargerFichier();

  if ( !sc )
    {
      error() << sc.message() << endmsg;
      return StatusCode( false, 0, CodesErreurs::ErreurLectureFichier,
			 "Impossible de lire le fichier" );
    }

  /*
   * Première étape : trouver une balise ouvrante.
   */

  string balise;
  char o_f;

  trouverOuvrante();

  sc = litBalise();

  balise = sc.message();
  o_f = static_cast< char >( sc.valeur() );

  trouverFermante();

  ElementDOM elDom( isolerAttributs( balise ) );
  
  if ( o_f == 'o' )
    {
      if ( balise[0] == '?' )
	{
	  linfo() << "On saute le header" << endmsg;
	  return lireDocRecursif();
	}
      else if ( balise[0] == '!' )
	{
	  linfo() << "On saute une partie commentée" << endmsg;
	  skipComment();
	  return lireDocRecursif();
	}
      else
	{
	  linfo() << "On ouvre " << balise << endmsg;
	  Pile.push( balise );
	}
    }
  else
    {
      if ( Pile.top() != balise )
	{
	  error() << "Balise fermante trouvée </" << balise
		  << "> alors qu'il faut fermer <"
		  << Pile.top() << ">" << endmsg;
	  donneesLues = false;
	  return StatusCode( false, 0, CodesErreurs::FermetureMauvaiseBalise,
			     "Fermeture de la mauvaise balise, ou balise ouvrante manquante" );
	}
      else
	{
	  linfo() << "On ferme " << balise << endmsg;
	  if ( balise == Parents.top() -> balise()  )
	    Parents.pop();
	  Pile.pop();
	  if ( Pile.empty() )
	    {
	      // On ajoute les enfants de chaque parent.
	      ElementDOM* par;
	      for (unsigned i(0); i < numeroElement; i++)
		{
		  if ( Arbre[i] -> parent() != 0 )
		    {
		      linfo() << Arbre[i] -> parent() -> balise()
			      << " a pour enfant "
			      << Arbre[i] -> balise() << endmsg;
		      par = const_cast< ElementDOM * >( Arbre[i] -> parent() );
		      par -> ajoutEnfant( Arbre[i] );
		    }
		}
	      
	      
	      //func() << "lireDocument -> fin" << endmsg;
	      return StatusCode( true, 0, 0, "Lecture terminée" );
	    }
	  return lireDocRecursif();
	}
    }

  sc = verifierContenuFutur( balise );

  elDom . setNumero( numeroElement++ );

  if ( sc )
    {
      // On a trouvé des données
      while ( Informations.find( balise ) != Informations.end() )
      	balise += '+';

      int i(index);
      string valeur("");
      while ( donnees[i] != '<' )
	{
	  valeur += donnees[i];
	  i++;
	}
      debug() << "Valeur : \'" << valeur << "\'" << endmsg;
      wipeBlank( valeur );
      elDom . setDonnees( valeur );
      elDom . setParent( Parents.top() );
      elDom . setADonnees( true );
      Arbre.push_back( new ElementDOM( elDom ) );
      Informations[ balise ] = elDom;
    }
  else if ( balise[ balise.size() - 1 ] == '/'  )
    {
      balise = elDom . balise();
      elDom . setParent( Parents.top() );
      Informations[ balise ] = elDom;
      Arbre.push_back( new ElementDOM( elDom ) );
      Pile.pop();
    }
  else
    {
      // On se trouve devant une balise qui contient des balises
      while ( Informations.find( balise ) != Informations.end() )
	balise += '+';

      elDom . setParent( Parents.top() );
      
      Informations[ balise ] = elDom;
      Arbre.push_back( new ElementDOM( elDom ) );
      Parents.push( Arbre.back() );
    }

  if ( Pile.size() != 0 )
    {
      //func() << "lireDocument -> fin" << endmsg;
      return lireDocRecursif();
    }

  //func() << "lireDocument -> fin" << endmsg;
  return StatusCode( true );
}

/**
 * Lit le document et s'arrête à la première balise ouvrante trouvée.
 *
 * @return StatusCode, \e true si une balise ouvrante a bien été
 * trouvée, auquel cas l'index est donné par valeur(). Si on a pas
 * trouvé de balise ouvrante, on renvoie \e false, le code d'erreur 10
 * et un message.
 */
StatusCode Parser::trouverOuvrante()
{
  /*Debug::*/empileNiveau( &elementaire );
  int ret(-1); 
  /*
   * La valeur -1 est assignee au cas ou on ne rencontre plus de <
   */
  for(unsigned i(index); i < taille; i++)
    /*
     * On commence avec la valeur donnee par l'index, derniere position
     * lue par l'index
     */
    {
      if(donnees[i]=='<')
	{
	  ret=i;
	  index=i+1; 
	  /*
	   * On met a jour l'index de lecture ssi elle trouve un <
	   */
	  break;
	}
    }
  /* 
   * Si la methode ne trouve plus de < elle ne met pas a jour l index de
   * lecture
   */
  if(ret==-1) //erreur
    {
      error() << "Début de balise non trouvée, Parser::trouverOuvrante"
	      << endmsg;
      return StatusCode( false, 0, CodesErreurs::DebutBaliseManquant,
			 "Début de balise non trouvée" );
    }
  /*Debug::*/depileNiveau();
  return StatusCode( true, ret );
}

/**
 * Lit le document et s'arrête à la fin de la balise courante.
 *
 * @return StatusCode, \e true si une balise fermante a bien été
 * trouvée, auquel cas l'index est donné par valeur(). Si on a pas
 * trouvé de balise fermante, on renvoie \e false, le code d'erreur 11
 * et un message.
 */
StatusCode Parser::trouverFermante()
{
  /*Debug::*/empileNiveau( &elementaire );
  int ret(-1); 
  /*
   * La valeur -1 est assignee au cas ou on ne rencontre plus de /
   */
  for(unsigned i(index);i<taille;i++)
    /*
     * On commence avec la valeur donnee par l'index, derniere position lue
     * par l'index
     */
    {
      if(donnees[i]=='>')
	{
	  ret=i;
	  index++;
	  /*
	   * On met a jour l'index de lecture
	   */
	  break;
	}
    }
  if(ret==-1) //erreur
    {
      error() << "Fin de balise non trouvée dans Parser::trouverFermante"
	      << endmsg;
      return StatusCode( false, 0, CodesErreurs::FinBaliseManquante,
			 "Fin de balise non trouvée" );
    }
  /*Debug::*/depileNiveau();
  return StatusCode( true, ret );
}

/**************************************************************************//**
 *
 * Permet de s'affranchir des ' ' entre deux balises, deux informations, ...
 *
 * FUNC  permet d'afficher les messages de début et de fin
 *
 *****************************************************************************/
void Parser::sauterBlanc()
{
  /*Debug::*/empileNiveau( &elementaire );
  func() << "sauterBlanc -> début" << endmsg;
  while ( (donnees[index]==32) || (donnees[index]==10))
    {
      index++;
    }
  func() << "sauterBlanc -> fin" << endmsg;
  /*Debug::*/depileNiveau();//dout.setLevel(etat_precedent);
}

/**************************************************************************//**
 *
 * Fait disparaître les ' ' à la fin d'une string lors de la récupération
 * depuis un fichier .xml
 *
 *****************************************************************************/
void Parser::wipeBlank(string& avecEspaces)
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "wipeBlank -> début" << endmsg;

  debug() << "\'" << avecEspaces << "\' -> ";

  trim( avecEspaces );
  /*
  if ( avecEspaces != "" )
    {
      unsigned i( avecEspaces.size() - 1 ), j(0);

      bool seulEspaces( true );

      for (j = 0; j <= i; j++)
	seulEspaces &= ( avecEspaces[j] == ' ' );

      if ( seulEspaces )
	{
	  debug() << "(uniquement espaces) ";
	  avecEspaces = "";
	}
      else
	{
	  j = 0;
	  while ( avecEspaces[i] == ' ' )
	    i--;
	  while ( avecEspaces[j] == ' ' )
	    j++;
	  string tmp("");
	  for (; j <= i; j++)
	    {
	      tmp += avecEspaces[j];
	    }
	  avecEspaces = tmp;
	}
    }
  */
  debug() << "\'" << avecEspaces << "\'" << endmsg;

  func() << "wipeBlank -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**************************************************************************//**
 *
 * Lit le contenu de la balise
 *
 * FUNC  permet d'afficher les messages de début et de fin
 *
 *****************************************************************************/
StatusCode Parser::litBalise()
{
  /*Debug::*/empileNiveau( &elementaire );
   char o_f;
  func() << "litBalise -> début" << endmsg;
  if(donnees[index]=='/') 
    /*
     * On a affaire a une balise fermante
     */
    {
      index += 1;
      o_f = 'f';
      /*
       * On oublie pas d'avancer l'index
       * C'est une fermante
       */
    }
  else 
    {
      o_f = 'o';
      /*
       * C'est une ouvrante
       */
    }
  
  string out("");
  while( donnees[index] != '>' )
    {
      out += donnees[index];
      index++;
    }

  func() << "litBalise -> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return StatusCode( true, static_cast< int >(o_f), 0, out );
}

/**************************************************************************//**
 *
 * Permet de sauter un commentaire
 *
 * FUNC  permet d'afficher les messages de début et de fin
 *
 *****************************************************************************/
StatusCode Parser::skipComment()
{
  /*Debug::*/empileNiveau( &elementaire );
  func() << "skipComment -> début" << endmsg;
  int ancienne_pos_index(index);
  /*
   * Memorise la position initiale de l'index
   */
  int temp( trouverOuvrante() );
  /*
   * La position de l'index est modifiee ici
   */
  if (temp==-1) index=ancienne_pos_index;
  /*
   * Plus de commentaire possible car pas de <
   */
  else
    /*
     * Il a trouve un <
     */
    {
      sauterBlanc();
      /*
       * Il saute les blancs et pointe le premier caractere apres <
       */
      if(donnees[index]=='!')
	/*
	 * On a affaire a un commentaire car commence par !
	 */
	{
	  int blabla(trouverFermante());
	  /*
	   * Cherche la fin du commentaire (il cherche >) et met à jour
	   l'index de lecture, job terminé
	  */
	  if(blabla==-1)
	    /*
	     * Cas ou le commentaire n a pas de fin -> Erreur !
	     */
	    {
	      error() << "Syntaxe de commentaire incorrecte dans Parser::skipComment()" << endmsg;
	      return StatusCode( false, 0, CodesErreurs::ErreurSyntaxeCommentaire,
				 "Erreur de syntaxe dans le commentaire" );
	    }
	}
      else
	{
	  /*
	   * Si le caractère n'est pas '!' il remet l'index ou il était,
	   * il n a pas eu affaire à un commentaire
	   */
	  index=ancienne_pos_index;
	}
    }
  func() << "skipComment -> fin" << endmsg;
  /*
   * Au final la position de l'index est modifiée que si un commentaire
   * a été trouvé et dans un tel cas on a "passé" le commentaire
   */
  /*Debug::*/depileNiveau();
  return StatusCode( true );
}

/**************************************************************************//**
 *
 * Gestion de la bonne structure du fichier .xml
 * Cherche le contenu de la balise suivante et le compare à la string b
 *
 *****************************************************************************/
StatusCode Parser::verifierContenuFutur(const string& future)
{
  /*Debug::*/empileNiveau( &elementaire );
  int mem_pos(index);
  skipComment();
  trouverOuvrante();
  StatusCode sc( litBalise() );
  index = mem_pos;
  /*Debug::*/depileNiveau();
  if ( sc.message() == future && static_cast< char >( sc.valeur() ) == 'f' )
    return StatusCode( true );
  else
    return StatusCode( false, 0, CodesErreurs::BaliseAttenduePasTrouvee,
		       "La balise suivante n'est pas celle attendue" );
}

/**
 * Lit la balise et construit un ElementDOM avec les éventuels
 * attributs présents. Est capable de lire une balise du type
 * \<contenu/\>
 */
ElementDOM Parser::isolerAttributs(string& balise)
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "isolerAttributs -> début" << endmsg;

  debug() << "Balise reçue : " << balise << endmsg;

  if ( balise.at(0) == '?' || balise.at(0) == '!' )
    {
      func() << "isolerAttributs -> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return ElementDOM();
    }
  
  string tmpBal(""), tmpAttr, tmpVal;

  size_t taille( balise.size() );

  bool baliseVide( balise[ taille - 1 ] == '/' );

  if ( baliseVide )
    balise = balise.substr(0, taille - 1);

  if ( balise.find( ' ' ) == string::npos )
    {
      tmpBal = balise;
      if ( baliseVide )
	balise += '/';
      func() << "isolerAttributs -> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return ElementDOM( tmpBal, Pile.size() );
    }

  vector< string > espacesSep, attrib;

  split( espacesSep, balise, is_space(), token_compress_on );

  tmpBal = espacesSep[ 0 ];
   
  espacesSep.erase( espacesSep.begin() );

  vector< string >::iterator it, debut( espacesSep.begin() ),
    fin( --espacesSep.end() );
  
  for ( it = debut; it != fin; it++ )
    {
      if ( count( it -> begin(), it -> end(), '\"' ) == 1 &&
	   *( it -> rbegin() ) != '\"' )
	{
	  *it += " " + *( it + 1 );
	  *( it + 1 ) = "eFFaceR";
	}
    }
  fin = remove_if( espacesSep.begin(), espacesSep.end(),
		   _1 == "eFFaceR" );
      
  espacesSep.assign( debut, fin );
  
  debug() << "Nom balise : " << tmpBal << endmsg;

  ElementDOM dom( tmpBal, Pile.size() );

  BOOST_FOREACH( string element, espacesSep )
    {
      split( attrib, element, is_any_of( "=" ), token_compress_on );

      if ( attrib . size() != 2 )
	{
	  if ( Pile.size() > 1 )
	    warning() << "Impossible d'extraire une paire attribut-valeur de \'"
		      << element << "\' dans la balise " << balise << endmsg;
	  continue;
	}
      
      tmpAttr = attrib[ 0 ];
      tmpVal  = attrib[ 1 ];
      
      replace_all( tmpVal, "\"", "" );
      
      debug() << "Ajout de l'attribut \'" << tmpAttr << "\' avec la valeur \'"
	      << tmpVal << "\' pour la balise " << tmpBal << endmsg;
      dom . ajoutAttribut( tmpAttr, tmpVal );
    }

  balise = dom . balise();

  /*
    while ( balise[i] != ' ' && i < taille )
    {
      tmpBal += balise[i];
      i++;
    }

  ElementDOM dom( tmpBal, Pile.size() );

  while ( balise[i+1] == ' ' && i < taille )
    i++;

  for (; i < taille; i++)
    {
      tmpAttr = "";
      tmpVal  = "";
      while ( balise[i] != '=' && i < taille )
	{
	  tmpAttr += balise[i];
	  i++;
	}
      while ( balise[i] != '\"' && i < taille )
	i++;
      i++;
      while ( i < taille )
	{
	  if ( balise[i] == '\"' ) break;
	  tmpVal += balise[i];
	  i++;
	}
      wipeBlank( tmpAttr );
      wipeBlank( tmpVal );
      debug() << "Ajout de l'attribut \'" << tmpAttr << "\' avec la valeur \'"
	      << tmpVal << "\' pour la balise " << tmpBal << endmsg;
      dom . ajoutAttribut( tmpAttr, tmpVal );
      while ( balise[i] == ' ' && i < taille )
	i++;
    }

  balise = tmpBal;
  */
  if ( baliseVide )
    balise += '/';

  lfunc() << dom . balise() << " - \'" << dom . donnees() << "\'";
  if ( dom . aAttributs() )
    {
      lfunc() << " (";
      dom . afficheAttributs( lfunc() );
      lfunc() << ")";
    }
  lfunc() << endmsg;

  func() << "isolerAttributs -> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return dom;
}
